import uuid
import functools
from redlock import RedLock, RedLockError
import celery as _celery
from celery.utils.log import get_task_logger

from .config import CELERY_BROKER_URL, CELERY_RESULT_BACKEND
from utils.db import db


logger = get_task_logger(__name__)


def insert_into_task(task_id, task_name):
    sql = """insert into {table_name} values ({task_id}, {task_name})""".format(task_id, task_name)
    r = db.execute(sql)
    if r.lastrowid:
        db.commit()
        logger.info('task insert success')
        return
    db.rollback()

def make_celery():

    class Celery(_celery.Celery):
        
        def task(self, *args_task, **opts_task):
            def decorator(func):
                sup = super(Celery, self).task

                @sup(*args_task, **opts_task)
                @functools.wraps(func)
                def wrapper(*args, **kwargs):
                    from utils.myredis import rs
                    from server.apis.task.api import Task

                    try:
                        # 在执行任务之前验证任务的执行结果，并加锁，保证任务只会执行一次
                        task_id = wrapper.request.id
                        with RedLock(task_id, connection_details=[rs], ttl=60):
                            celery_task = Task.get(task_id=task_id)
                            # 若任务已经执行，直接结束
                            if celery_task and Task.is_done(task_id=task_id):
                                return
                            # 执行任务
                            func(*args, **kwargs)
                    except RedLockError:
                        return
                    except Exception as e:
                        # 任务重试，最多重试3此，每次间隔5秒钟
                        wrapper.retry(e=e, args=args, kwargs=kwargs, countdown=5, max_retries=3)
                    else:
                        if celery_task:
                            # 执行成功，更新status
                            Task.task_down()

                return wrapper
            return decorator

    celery_app = Celery('event-celery',
                        broker=CELERY_BROKER_URL,
                        backend=CELERY_RESULT_BACKEND)
    celery_app.config_from_object('celery_app.config')
    task_base = celery_app.Task

    class ContextTask(task_base):
        abstract = True

        def __call__(self, *args, **kwargs):
            from flask import current_app
            with current_app.app_context():
                return task_base.__call__(self, *args, **kwargs)

        def delay(self, *args, **kwargs):
            task_id = str(uuid.uuid4())
            # 执行写入数据库的function
            insert_into_task(
                task_id=task_id,
                task_name=self.__name__
            )

    celery_app.Task = ContextTask

    return celery_app


celery = make_celery()
